﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using Aribaccio.Security;

namespace Aribaccio.Copyright
{
    /// <summary>
    /// 注册版权期限类
    /// </summary>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class RegisterCopyLimits : RegisterCopyBase, IRegisterCopyLimits
    {
        /// <summary>
        /// 加密解密对象
        /// </summary>
        protected ISecurity SecurityKey { get; set; }
        /// <summary>
        /// 注册序列号拼接符
        /// </summary>
        public string Concatenator { get { return this.RegisterPara.Concatenator; } }

        /// <summary>
        /// 构造函数
        /// </summary>
        public RegisterCopyLimits(ISecurity argSecurityKey) : base()
        {
            this.SecurityKey = argSecurityKey;
        }

        /// <summary>
        /// 初始化写入注册表项
        /// </summary>
        /// <returns></returns>
        protected bool InitRegisterSubKey()
        {
            bool bResult = WriteRegisterSubKey();
            if (bResult) { bResult = InitRegisterSubKeyValue(); }
            return bResult;
        }

        /// <summary>
        /// 初始化写入注册表项默认值
        /// </summary>
        /// <returns></returns>
        protected bool InitRegisterSubKeyValue()
        {
            bool bResultFlag = false;
            try
            {
                //该项必须已存在
                RegistryKey registSubKey = RegistKey.OpenSubKey(RegistryKeyPathName, true);
                if (registSubKey != null)
                {
                    this.RegisterPara.SerialNum = this.SecurityKey.Encrypt(this.RegisterPara.SerialNum);
                    //写入序列号和终止时间
                    registSubKey.SetValue(this.RegistrySubKeySerialNum, this.SecurityKey.Encrypt(this.RegisterPara.SerialLimit));
                    //写入起始时间
                    registSubKey.SetValue(this.RegistrySubKeyStart, this.SecurityKey.Encrypt(this.RegisterTimeSatrt.ToString()));
                    //写入最后访问时间
                    registSubKey.SetValue(this.RegistrySubKeyLast, this.SecurityKey.Encrypt(this.RegisterTimeSatrt.ToString()));
                    bResultFlag = true;
                    registSubKey.Close();
                }
            }
            catch (Exception) { bResultFlag = false; }
            finally { RegistKey.Close(); }
            return bResultFlag;
        }

        /// <summary>
        /// 检查注册版权期限期限状态
        /// </summary>
        /// <returns>剩余期限天数</returns>
        public IRegisterCopyMsg CheckRegisterCopyLimits()
        {
            RegisterCopyMsg resultMsg = new RegisterCopyMsg();
            //判断是否存在该项，不存在初始化，存在写入最后一次访问时间
            bool bResult = ExistRegistryKey(RegistryKeyName);
            if (!bResult) { bResult = InitRegisterSubKey(); }
            if (bResult)
            {
                //获取注册相关序列号和时间（序列号、起始日期、终止日期、最后访问日期）
                RegisterCopyPara serialItem = ReadRegisterSubKeyValueCopyItem(RegistrySubKeySerialNum);
                DateTime dateTimeStart = ReadRegisterSubKeyValueTime(RegistrySubKeyStart);
                DateTime dateTimeLast = ReadRegisterSubKeyValueTime(RegistrySubKeyLast);
                //读取加密机器码，并校验
                string strMachineCode = this.ReadMachineCode();
                bool bIsCheckRegister = true;
                if (serialItem.SerialNum != strMachineCode) { bIsCheckRegister = false; }
                //检测校验日期是否有效，终止日期>当前日期>起始日期 并且 当前日期>最后访问日期，才允许正常运行，一个都不满足就推出程序
                int resultLimitSurplusDays = 0;
                if (DateTime.Now >= dateTimeStart && DateTime.Now <= serialItem.TimeLimit && DateTime.Now >= dateTimeLast)
                {
                    TimeSpan ts = serialItem.TimeLimit - DateTime.Now;
                    resultLimitSurplusDays = ts.Days > 0 ? ts.Days : 1;
                }
                //写入最后一次访问时间
                WriteDateTimeLast(false);
                //判断并返回
                //已注册，有效期之内
                if (bIsCheckRegister && resultLimitSurplusDays > 0)
                {
                    resultMsg.Status = RegisterCopyStatus.OK;
                    resultMsg.StatusMsg = "Registered and valid";
                }
                //已注册，有效期已过
                else if (bIsCheckRegister && resultLimitSurplusDays < 0)
                {
                    resultMsg.Status = RegisterCopyStatus.OKRegister;
                    resultMsg.StatusMsg = "Registered, expired";
                }
                //未注册，试用期之内
                else if (!bIsCheckRegister && resultLimitSurplusDays > 0)
                {
                    resultMsg.Status = RegisterCopyStatus.OKTimeLimits;
                    resultMsg.StatusMsg = "Unregistered, within the probation period";
                }
                //未注册，试用期已过
                else
                {
                    resultMsg.Status = RegisterCopyStatus.None;
                    resultMsg.StatusMsg = "No registration, probation period has expired";
                }
                resultMsg.LimitSurplusDays = resultLimitSurplusDays;
            }
            //注册失败
            else { resultMsg.Status = RegisterCopyStatus.None; resultMsg.StatusMsg = "Registration failure"; }
            return resultMsg;
        }

        /// <summary>
        /// 写入注册序列号
        /// </summary>
        /// <param name="argSerialNum">加密序列号字符串（包含结束/终止时间）</param>
        /// <returns></returns>
        public IRegisterCopyMsg WriteSerialNum(string argSerialNum)
        {
            RegisterCopyMsg resultMsg = new RegisterCopyMsg() { Status = RegisterCopyStatus.None };
            if (string.IsNullOrWhiteSpace(argSerialNum)) { resultMsg.StatusMsg = "The registration serial number cannot be empty"; return resultMsg; }
            try
            {
                //先解密,字符串分割,校验
                string strSerialNum = this.SecurityKey.Decrypt(argSerialNum);
                if (!string.IsNullOrWhiteSpace(strSerialNum))
                {
                    string[] strItems = strSerialNum.Split(this.RegisterPara.Concatenator.ToCharArray());
                    //校验格式(注册失败，无效的注册序列号，格式无效)
                    if (strItems == null || strItems.Length < 2)
                    {
                        resultMsg.StatusMsg = "Registration failed, invalid registration serial number, invalid format";
                        return resultMsg;
                    }
                    //校验日期期限是否有效(注册失败，无效的注册序列号,日期格式无效)
                    DateTime resultDateTime = default(DateTime);
                    if (!DateTime.TryParse(strItems[strItems.Length - 1], out resultDateTime))
                    {
                        resultMsg.StatusMsg = "Registration failed, invalid registration serial number, invalid date format";
                        return resultMsg;
                    }
                    //获取校验序列号，并解密为机器码
                    string resultMachineCode = string.Empty;
                    for (int i = 0; i < strItems.Length - 1; i++) { resultMachineCode += strItems[i]; }
                    //读取加密机器码，并校验（注册失败，无效的注册序列号，密机器无效）
                    string strMachineCode = this.ReadMachineCode();
                    if (resultMachineCode != strMachineCode)
                    {
                        resultMsg.StatusMsg = "Registration failure, invalid registration serial number, cipher machine invalid";
                        return resultMsg;
                    }
                    //校验是否存在该项，不存在初始化，存在写入最后一次访问时间
                    if (!ExistRegistryKey(RegistryKeyName)) { InitRegisterSubKey(); }
                    //写入注册表
                    bool bResult = WriteRegisterSubKeyValue(RegistrySubKeySerialNum, argSerialNum);
                    resultMsg.Status = bResult ? RegisterCopyStatus.OK : RegisterCopyStatus.None;
                    //注册成功          注册失败，写入失败
                    resultMsg.StatusMsg = bResult ? "Successful registration " : "Failed to register or write. Procedure";
                }
                //注册失败，无效的注册序列号
                else
                {
                    resultMsg.StatusMsg = "Registration failed, invalid registration serial number";
                    return resultMsg;
                }
            }
            catch (Exception ep)
            {
                resultMsg.Status = RegisterCopyStatus.None;
                resultMsg.StatusMsg = "Registration failure：" + ep.Message;
            }
            return resultMsg;
        }

        /// <summary>
        /// 写入注册最后访问时间
        /// </summary>
        public bool WriteDateTimeLast(string argDateTime)
        {
            DateTime dtDate = default(DateTime);
            if (!DateTime.TryParse(argDateTime, out dtDate)) { dtDate = default(DateTime); }
            return WriteDateTimeLast(dtDate);
        }

        /// <summary>
        /// 写入注册最后访问时间
        /// </summary>
        /// <param name="argDateTime"></param>
        /// <returns></returns>
        public bool WriteDateTimeLast(DateTime? argDateTime)
        {
            if (argDateTime == null || argDateTime == default(DateTime)) { argDateTime = DateTime.Now; }
            //校验是否存在该项，不存在初始化，存在写入最后一次访问时间
            if (!ExistRegistryKey(RegistryKeyName)) { InitRegisterSubKey(); }
            //加密后，写入注册最后访问时间
            return WriteRegisterSubKeyValue(RegistrySubKeyLast, this.SecurityKey.Encrypt(argDateTime.ToString()));
        }

        /// <summary>
        /// 写入注册最后访问时间
        /// </summary>
        public bool WriteDateTimeLast(bool argIsCheck = true)
        {
            try
            {
                //是否开启校验，校验是否存在该项，不存在初始化，存在写入最后一次访问时间
                if (argIsCheck && !ExistRegistryKey(RegistryKeyName)) { InitRegisterSubKey(); }
                //获取当前运行的可执行文件完整路径
                FileInfo fileInfo = new FileInfo(this.GetType().Assembly.Location);
                //获取上次访问的时间
                DateTime dateTime = fileInfo.LastAccessTime;
                if (dateTime == default(DateTime)) { dateTime = DateTime.Now; }
                //加密后，写入注册最后访问时间
                return WriteRegisterSubKeyValue(RegistrySubKeyLast, this.SecurityKey.Encrypt(dateTime.ToString()));
            }
            catch (Exception) { return false; }
        }

        /// <summary>
        /// 生成注册序列号
        /// </summary>
        /// <param name="argMachineCode">机器码</param>
        /// <param name="argTimeLimit">日期期限（终止时间）</param>
        /// <returns></returns>
        public string CreateSerialNum(string argMachineCode, string argTimeLimit)
        {
            DateTime dtDate = default(DateTime);
            if (!DateTime.TryParse(argTimeLimit, out dtDate)) { dtDate = default(DateTime); }
            return CreateSerialNum(argMachineCode, dtDate);
        }

        /// <summary>
        /// 生成注册序列号
        /// </summary>
        /// <param name="argMachineCode">机器码</param>
        /// <param name="argTimeLimit">日期期限（终止时间）</param>
        /// <returns></returns>
        public string CreateSerialNum(string argMachineCode, DateTime argTimeLimit)
        {
            if (argTimeLimit == default(DateTime)) { return ""; }
            if (string.IsNullOrWhiteSpace(argMachineCode)) { return ""; }
            //拼接字符串
            string strRegisterSerialNum = string.Concat(argMachineCode, Concatenator, argTimeLimit.ToShortDateString());
            //加密字符串,在返回
            return this.SecurityKey.Encrypt(strRegisterSerialNum);
        }

        /// <summary>
        /// 生成注册序列号
        /// </summary>
        /// <returns></returns>
        public string CreateSerialNum(IRegisterCopyPara argSerialItem)
        {
            //先加密字符串，在返回
            return this.SecurityKey.Encrypt(argSerialItem.SerialLimit);
        }

        /// <summary>
        /// 读取机器码
        /// </summary>
        /// <returns>机器码</returns>
        public string ReadMachineCode()
        {
            //先加密字符串，在返回
            return this.SecurityKey.Encrypt(this.ReadSystemDiskNo());
        }

        /// <summary>
        /// 读取注册表项键值-日期类型
        /// </summary>
        /// <param name="argKeyName"></param>
        /// <returns></returns>
        protected DateTime ReadRegisterSubKeyValueTime(string argKeyName)
        {
            DateTime resultDateTime = default(DateTime);
            object? resultObj = ReadRegisterSubKeyValue(argKeyName);
            if (resultObj != null)
            {
                string strResult = this.SecurityKey.Decrypt(resultObj.ToString());
                if (!string.IsNullOrWhiteSpace(strResult)) { DateTime.TryParse(strResult, out resultDateTime); }
            }
            return resultDateTime;
        }

        /// <summary>
        /// 读取注册表项键值-版权信息项
        /// </summary>
        /// <returns></returns>
        protected RegisterCopyPara ReadRegisterSubKeyValueCopyItem(string argKeyName)
        {
            RegisterCopyPara resultItem = new RegisterCopyPara() { SerialNum = string.Empty };
            object? resultObj = ReadRegisterSubKeyValue(argKeyName);
            if (resultObj != null && !string.IsNullOrWhiteSpace(resultObj.ToString()))
            {
                //先解密，在分割
                string strResult = this.SecurityKey.Decrypt(resultObj.ToString());
                string[] strItems = strResult.Split(this.RegisterPara.Concatenator.ToCharArray());
                if (strItems != null && strItems.Length > 1)
                {
                    for (int i = 0; i < strItems.Length - 1; i++)
                    {
                        resultItem.SerialNum += strItems[i];
                    }
                    resultItem.TimeLimit = Convert.ToDateTime(strItems[strItems.Length - 1]);
                }
            }
            return resultItem;
        }

    }
}
